package com.ly.judge.service.impl;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.StatsCmd;
import com.ly.common.core.constants.Constants;
import com.ly.common.core.constants.JudgeConstants;
import com.ly.common.core.enums.CodeRunStatus;
import com.ly.judge.callback.DockerStartResultCallback;
import com.ly.judge.callback.StatisticsCallback;
import com.ly.judge.config.DockerSandBoxPool;
import com.ly.judge.domain.CompileResult;
import com.ly.judge.domain.SandBoxExecuteResult;
import com.ly.judge.service.ISandboxPoolService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * 沙箱池服务实现类
 * 负责管理Docker沙箱容器池，提供Java代码的编译和执行功能
 */
@Service
@Slf4j
public class SandboxPoolServiceImpl implements ISandboxPoolService {

    /**
     * Docker沙箱容器池，用于管理可用的Docker容器
     */
    @Autowired
    private DockerSandBoxPool sandBoxPool;

    /**
     * Docker客户端，用于与Docker守护进程交互
     */
    @Autowired
    private DockerClient dockerClient;

    /**
     * 当前使用的容器ID
     */
    private String containerId;

    /**
     * 用户代码文件路径
     */
    private String userCodeFileName;

    /**
     * 代码执行时间限制（秒），默认5秒
     */
    @Value("${sandbox.limit.time:5}")
    private Long timeLimit;

    /**
     * 执行Java代码
     * @param userId 用户ID
     * @param userCode 用户提交的Java代码
     * @param inputList 输入参数列表
     * @return 执行结果对象
     */
    @Override
    public SandBoxExecuteResult exeJavaCode(Long userId, String userCode, List<String> inputList) {
        // 1. 从容器池获取一个可用容器
        containerId = sandBoxPool.getContainer();

        // 2. 创建用户代码文件
        createUserCodeFile(userCode);

        // 3. 编译代码
        CompileResult compileResult = compileCodeByDocker();
        if (!compileResult.isCompiled()) {
            // 3.1 编译失败，归还容器并清理文件
            sandBoxPool.returnContainer(containerId);
            deleteUserCodeFile();
            // 3.2 返回编译失败结果
            return SandBoxExecuteResult.fail(CodeRunStatus.COMPILE_FAILED, compileResult.getExeMessage());
        }

        // 4. 执行代码并返回结果
        return executeJavaCodeByDocker(inputList);
    }

    /**
     * 创建用户代码文件
     * @param userCode 用户提交的Java代码
     */
    private void createUserCodeFile(String userCode) {
        // 1. 获取代码目录路径
        String codeDir = sandBoxPool.getCodeDir(containerId);
        log.info("user-pool路径信息：{}", codeDir);

        // 2. 构建代码文件路径
        userCodeFileName = codeDir + File.separator + JudgeConstants.USER_CODE_JAVA_CLASS_NAME;

        // 3. 删除已存在的旧文件
        if (FileUtil.exist(userCodeFileName)) {
            FileUtil.del(userCodeFileName);
        }

        // 4. 写入用户代码到文件
        FileUtil.writeString(userCode, userCodeFileName, Constants.UTF8);
    }

    /**
     * 使用Docker容器编译Java代码
     * @return 编译结果对象
     */
    private CompileResult compileCodeByDocker() {
        // 1. 创建编译命令
        String cmdId = createExecCmd(JudgeConstants.DOCKER_JAVAC_CMD, null, containerId);

        // 2. 准备回调处理器
        DockerStartResultCallback resultCallback = new DockerStartResultCallback();
        CompileResult compileResult = new CompileResult();

        try {
            // 3. 执行编译命令
            dockerClient.execStartCmd(cmdId)
                    .exec(resultCallback)
                    .awaitCompletion();

            // 4. 处理编译结果
            if (CodeRunStatus.FAILED.equals(resultCallback.getCodeRunStatus())) {
                compileResult.setCompiled(false);
                compileResult.setExeMessage(resultCallback.getErrorMessage());
            } else {
                compileResult.setCompiled(true);
            }
            return compileResult;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 使用Docker容器执行Java代码
     * @param inputList 输入参数列表
     * @return 执行结果对象
     */
    private SandBoxExecuteResult executeJavaCodeByDocker(List<String> inputList) {
        // 1. 初始化执行结果记录
        List<String> outList = new ArrayList<>(); // 输出结果列表
        long maxMemory = 0L;  // 最大内存使用量
        long maxUseTime = 0L; // 最大执行时间

        // 2. 遍历所有输入用例
        for (String inputArgs : inputList) {
            // 2.1 创建执行命令
            String cmdId = createExecCmd(JudgeConstants.DOCKER_JAVA_EXEC_CMD, inputArgs, containerId);

            // 2.2 准备计时器和资源监控
            StopWatch stopWatch = new StopWatch();
            StatsCmd statsCmd = dockerClient.statsCmd(containerId);
            StatisticsCallback statisticsCallback = statsCmd.exec(new StatisticsCallback());

            // 2.3 开始执行代码
            stopWatch.start();
            DockerStartResultCallback resultCallback = new DockerStartResultCallback();
            try {
                // 2.3.1 执行命令（带超时限制）
                dockerClient.execStartCmd(cmdId)
                        .exec(resultCallback)
                        .awaitCompletion(timeLimit, TimeUnit.SECONDS);

                // 2.3.2 处理执行失败情况
                if (CodeRunStatus.FAILED.equals(resultCallback.getCodeRunStatus())) {
                    return SandBoxExecuteResult.fail(CodeRunStatus.NOT_ALL_PASSED);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            // 2.4 停止计时和监控
            stopWatch.stop();
            statsCmd.close();

            // 2.5 记录执行数据
            long userTime = stopWatch.getLastTaskTimeMillis();
            maxUseTime = Math.max(userTime, maxUseTime);
            Long memory = statisticsCallback.getMaxMemory();
            if (memory != null) {
                maxMemory = Math.max(maxMemory, memory);
            }
            outList.add(resultCallback.getMessage().trim());
        }

        // 3. 清理资源
        sandBoxPool.returnContainer(containerId);
        deleteUserCodeFile();

        // 4. 封装并返回执行结果
        return getSanBoxResult(inputList, outList, maxMemory, maxUseTime);
    }

    /**
     * 创建Docker执行命令
     * @param javaCmdArr 基础命令数组
     * @param inputArgs 输入参数
     * @param containerId 容器ID
     * @return 命令ID
     */
    private String createExecCmd(String[] javaCmdArr, String inputArgs, String containerId) {
        // 1. 处理输入参数
        if (!StrUtil.isEmpty(inputArgs)) {
            String[] inputArray = inputArgs.split(" ");
            javaCmdArr = ArrayUtil.append(JudgeConstants.DOCKER_JAVA_EXEC_CMD, inputArray);
        }

        // 2. 创建并返回Docker执行命令
        ExecCreateCmdResponse cmdResponse = dockerClient.execCreateCmd(containerId)
                .withCmd(javaCmdArr)
                .withAttachStderr(true)
                .withAttachStdin(true)
                .withAttachStdout(true)
                .exec();
        return cmdResponse.getId();
    }

    /**
     * 封装沙箱执行结果
     * @param inputList 输入列表
     * @param outList 输出列表
     * @param maxMemory 最大内存使用量
     * @param maxUseTime 最大执行时间
     * @return 执行结果对象
     */
    private SandBoxExecuteResult getSanBoxResult(List<String> inputList, List<String> outList,
                                                 long maxMemory, long maxUseTime) {
        // 检查输入输出数量是否匹配
        if (inputList.size() != outList.size()) {
            return SandBoxExecuteResult.fail(CodeRunStatus.NOT_ALL_PASSED, outList, maxMemory, maxUseTime);
        }
        return SandBoxExecuteResult.success(CodeRunStatus.SUCCEED, outList, maxMemory, maxUseTime);
    }

    /**
     * 删除用户代码文件
     */
    private void deleteUserCodeFile() {
        FileUtil.del(userCodeFileName);
    }
}
